<?php

namespace App\Handlers\Commands;

use App\Commands\DeleteVulnerability as DeleteVulnerabilityCommand;
use App\Entities\ScannerApp;
use App\Entities\Vulnerability;
use App\Exceptions\ActionNotPermittedException;
use App\Exceptions\VulnerabilityNotFoundException;
use App\Exceptions\InvalidInputException;
use App\Policies\ComponentPolicy;
use App\Repositories\VulnerabilityRepository;
use Doctrine\ORM\EntityManager;
use Exception;

class DeleteVulnerability extends CommandHandler
{
    /** @var VulnerabilityRepository */
    protected $vulnerabilityRepository;

    /** @var EntityManager */
    protected $em;

    /**
     * DeleteVulnerability constructor.
     *
     * @param VulnerabilityRepository $vulnerabilityRepository
     * @param EntityManager $em
     */
    public function __construct(VulnerabilityRepository $vulnerabilityRepository, EntityManager $em)
    {
        $this->vulnerabilityRepository = $vulnerabilityRepository;
        $this->em                      = $em;
    }

    /**
     * Process the DeleteVulnerability command.
     *
     * @param DeleteVulnerabilityCommand $command
     * @return Vulnerability
     * @throws ActionNotPermittedException
     * @throws VulnerabilityNotFoundException
     * @throws InvalidInputException
     * @throws Exception
     */
    public function handle(DeleteVulnerabilityCommand $command)
    {
        // Get the authenticated User
        $requestingUser = $this->authenticate();

        $id      = $command->getId();
        $confirm = $command->isConfirm();
        // Check that we have the required input
        if (!isset($id, $confirm)) {
            throw new InvalidInputException("One or more required members are not set on the command");
        }

        // Get the vulnerability instance from the database
        /** @var Vulnerability $vulnerability */
        $vulnerability = $this->vulnerabilityRepository->find($id);
        if (empty($vulnerability) || !($vulnerability instanceof Vulnerability)) {
            throw new VulnerabilityNotFoundException("There is no existing Vulnerability with the given ID");
        }

        // Make sure we are deleting a Vulnerability on a Ruggedy ScannerApp. Currently this is the only place where
        // deleting Vulnerabilities is enabled
        if (!$vulnerability->getFile()->getWorkspaceApp()->isRuggedyApp()) {
            throw new ActionNotPermittedException(
                "Cannot delete Vulnerabilities on any WorkspaceApp other than the Ruggedy WorkspaceApp"
            );
        }

        // Make sure the User has permission to delete a Vulnerability from a WorkspaceApp
        if ($requestingUser->cannot(ComponentPolicy::ACTION_DELETE, $vulnerability)) {
            throw new ActionNotPermittedException(
                "The requesting User does not have permission to delete Vulnerabilities"
            );
        }

        // If deletion has been confirmed then set the deleted flag and persist the changes
        if ($command->isConfirm()) {
            $this->em->remove($vulnerability);
            $this->em->flush($vulnerability);
        }

        return $vulnerability;
    }
}